第9週 マクロ・ファイル操作
今回はマクロ処理について学ぶよ!
マクロとは
C言語におけるマクロとは、プログラム中の文字をあらかじめ定義した規則にしたがって置換する機能のことをいいます。C言語におけるマクロには定数マクロ(オブジェクト形式マクロとも呼ばれる)と、関数マクロの2種類があります。
どちらも#defineで定義することができ、Cプログラムがコンパイルされる直前にプリプロセッサ(他の言語ではマクロ展開器とも呼ぶ)によって展開されます。
定数マクロ
定数マクロは、#define 置換したい文字列 置換後の文字列で定義します。
マクロが展開されるとプログラムのソースコード内のマクロ(置換したい文字列)が全て置換後の文字列として扱われます。
例えば、#define N 100と書かれるとそれ以降のプログラム中のNという文字列は全て100と書かれているものとして扱われるわけです。変数とは違い、実行前や実行時にメモリを用意したりするオーバヘッドが無いのが特徴です。
基本的には、グローバルで扱いたい定数をマクロで記述します。また、C言語におけるマナーとしてマクロ名は全て大文字にしなければなりません。
次の例は定数マクロを使ってプログラム内で用いる文字列の長さを定義しているプログラムです。
code:macro.c
int main(void) {
fgets(buf, BUFSIZE, stdin);
puts(buf);
}
このプログラム内でなぜchar buf[1024];と宣言しないかについては変数と代入の捉え方を読みましょう。プログラム中の値を変数やマクロで抽象化することは基本的には良いことです。 変数とマクロのどちらを使って抽象化するかに関しては最初は迷うかもしれませんが、基本的にはマクロは変数では表現できないときにのみ用いるようにしましょう。
上の例に関しては変数で表現できなくはないですが、main以外の関数でも文字列を用意する場合があることなどを考えるとマクロを使うのが適切です。書き慣れていくとマクロを使うのが適切であるパターンが分かってきます。文字列の長さはその一つです。
関数マクロ
関数マクロは定数マクロと同様にプリプロセッサによってコンパイル前に展開されるマクロですが、関数のように引数を取ることができます。
よく使われるのは次のmaxマクロです。
code:max.c
int main(void) {
int a = 5, b = 7;
printf("%d\n", MAX(a, b));
}
このプログラムはMAX(a, b)式がコンパイル前に(a > b ? a : b)に展開されます。
関数マクロは、型が存在しないため実行時エラーが多発します。また、エラーが出ないにしても意図しない挙動を起こすことが多々あります。(第2週 型と演算子の挑戦問題参照。) 定数マクロ同様に関数マクロでしか書けないような状況ではない限り使わないのが推奨されます。
ファイル操作
今まで扱っていた処理データや結果データは、処理を終了したりパソコンの電源を切ったりすると消えてしまいます(※1)。実行終了後も保持しておきたいプログラムなどは、電源を切ってもデータが失われない”ファイル”に保存しておけば、見たいとき即座に処理データを見ることができます(※2)。ここではファイルのデータを読み書きする方法を説明します。
※1: 処理を終了したり電源を切ったりするとデータの内容が失われるメモリのことを揮発性メモリと言います。主記憶とか。脳みそとか
※2: 処理を終了したり電源を切ったりしてもデータの内容が失われないメモリのことを不揮発性メモリと言います。ハードディスクとか。ファイルはここに作成・保存されます。
ファイル入出力の流れ
ファイルの入出力は主に以下の手順を踏んで行います。
1. ファイル構造体のポインタ作成
2. ファイルを開く
3. ファイルの読み書き
4. ファイルを閉じる
順番に見ていきましょう。
1. ファイル構造体のポインタ作成
ファイルの入出力を行うには、まずファイル構造体のポインタを作成する必要があります。
大体こんな感じ↓
code:file
FILE *変数名;
この一文を記述すると、ファイル構造体のポインタが作成されます。
2. ファイルを開く
ファイルを開くにはfopen()関数を使います。
使い方はこんな感じ↓
code:file_open
FILE型のポインタ変数 = fopen("ファイル名","オープンモード");
ファイル名は開きたいファイルのファイル名を記述してください。オープンモードではファイルをどのように開くかを決めます。
モードの種類はこんな感じ↓
table:fopen_mode
モード 動作 ファイルがある場合 ファイルがない場合
"r" 読み出し専用 変化なし 失敗
"w" 書き込み専用 ファイルの上書き 新規作成
"a" 追加書き込み専用 ファイルの最後に追加 新規作成
"r+" 読み込みと書き込み 変化なし 失敗
"w+" 書き込みと読み込み ファイル上書き 新規作成
"a+" 読み込みと追加書き込み ファイルの最後に追加 新規作成
fopen()関数はファイルを開くと様々なファイル情報をもつFILE型のポインタ変数を返します。このFILE型のポインタ変数のことをファイルポインタといいます。ここで正しく開くことができなかった場合は失敗と見なされてNULLを返します。
4. ファイルを閉じる
ファイルの読み書きの前にファイルの閉じ方を説明します。ファイルを閉じるにはfclose()関数を使います。
使い方はこんな感じ↓
code:file_close
fclose(ファイルポインタ);
fopen()関数によって開いたファイルをfclose()関数で閉じることができます。ファイルを開いて閉じるプログラムは以下のようになります。
code:file.c
int main(){
//ファイル構造体へのポインタを宣言
FILE *fp;
//ファイルを書き込みモードで開く
fp = fopen("test.dat","w");
//ファイルオープンに失敗した場合
if(fp == NULL){
printf("ファイルオープン失敗\n");
return -1;
}
//ファイルを閉じる
fclose(fp);
return 0;
}
ここではtest.datというデータファイルを書き込みモードで開いて閉じる動作を行っています。
3. ファイルの読み書き
ファイルの読み書きを行う関数は何個かありますが、ここではfgets()関数に関して説明します。fgets()関数はファイルポインタで指定したファイルから読み取る文字数だけ読み取り、文字配列に格納する関数です。読み取る文字数にはヌル文字も含まれるので読み取る文字数の最大値は指定した数値-1になります。fgets()関数のプログラムを例を用いて表してみましょう。
code:fgets.c
int main(){
//ファイル構造体へのポインタを宣言
FILE *fp;
//ファイル名を読み込みモードで開く
//ファイル名は適当に自分でつけてね
fp = fopen("ファイル名","r");
//ファイルオープンに失敗した時
if(fp == NULL){
fprintf(stderr, "ファイルオープン失敗\n");
return -1;
}
//fgetsの戻り値がnullになるまで続ける
//strにファイルから256バイトを取得して格納
while((fgets(str,256,fp))!=NULL){
//格納された文字を出力
printf("%s",str);
}
//ファイルを閉じる
fclose(fp);
return 0;
}
ここではこのプログラムのソースファイルが出力されるようになっています。ファイルの読み書きに関しては色々な記述方法があるため、必要に応じて調べるようにしてください(丸投げ)。